﻿
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using ZYLFramework.Manage;
using ZYLFramework.UI;
using Random = UnityEngine.Random;

namespace Game.UI
{
    public enum AlgorithmModel
    {
        BFS,
        Dijkstra,
        AStart
    }
    public class AStartPanelCtrl:UICtrl
    {
        public AStartPanelView View;
        public AStartPanelModel Model;
        public List<MapItem> MapItemList;
        public List<int> Path;

        public const int MaxX = 19;
        public const int MaxY = 9;
        public int TargetPos;
        public int StartPos;
        public AlgorithmModel AlgorithmModel = AlgorithmModel.AStart;
        
        private delegate void CheckPosDelegate(Vector2 v);
        private delegate List<int> GetAroundDelegate(int v);
        public override void Init()
        {
            View = BaseView as AStartPanelView;
            Model = BaseModel as AStartPanelModel;
            MapItemList = new List<MapItem>();
            Path = new List<int>();
            StartPos = Pos2Index(3, 3);
            if (View != null && Model != null)
            {
                
                for (int i = 0; i <(MaxX*MaxY); i++)
                {
                    var mapItem = View.NewMapItem();
                    var index = i;
                    var info = new MapItemInfo();
                    mapItem.MapItemBtn.onClick.AddListener(delegate
                    {
                        if (Model.MapItemInfoList[index].MapState == MapState.Occupied)
                        {
                            View.Text.text = "无效的目标地点，请选择有效的目标地点";
                            return; 
                        }
                        TargetPos = index;
                        Path.Clear();
                        Model.ClearMapInfoStepsIndex();
                        switch (AlgorithmModel)
                        {
                           case AlgorithmModel.Dijkstra:
                               Dijkstra();
                               break;
                           case AlgorithmModel.BFS:
                               BFS();
                               break;
                           case AlgorithmModel.AStart:
                               AStart();
                               break;
                        }
                        RefreshMap();
                    });

                    MapItemList.Add(mapItem);
                    Model.MapItemInfoList.Add(info);
                } 
            }
            InitMap();
            ShowMapInfo();
            RefreshMap();
        }

        public void InitMap()
        {
            // GenMapFromConfig(); 
            switch (AlgorithmModel)
            {
                case AlgorithmModel.AStart:
                case AlgorithmModel.Dijkstra:
                    GenRandomCost();
                    GenRandomTransmission();
                    break;
                case AlgorithmModel.BFS:
                    GenRandomObstacles();
                    break;
            }

            for (int i = 0; i < Model.MapItemInfoList.Count; i++)
            {
                var info = Model.MapItemInfoList[i];
                if (i == StartPos)
                {
                    info.Cost = 0;
                } 
            }
        }

        public void RefreshMap()
        {
            for (int x = 0; x < MaxX; x++)
            {
                for (int y = 0; y < MaxY; y++)
                {
                    var index = Pos2Index(x, y);
                    var info = Model.MapItemInfoList[index];
                    var mapItem = MapItemList[index];
                    var image = mapItem.Go.GetComponent<Image>();
                    image.color = info.MapState == MapState.Empty ? Color.white : Color.grey;

                    if (TargetPos == index)
                    {
                       image.color = Color.blue; 
                    }

                    if (StartPos == index)
                    {
                       image.color = Color.red; 
                    }
                    
                    if (info.TransmissionIndex != -1)
                    {
                        image.color = Color.yellow; 
                    }

                    var text = mapItem.StepsText;
                    text.text = AlgorithmModel == AlgorithmModel.BFS?info.StepsIndex.ToString():info.Cost.ToString();
                } 
            }

            foreach (var v in Path)
            {
                var mapItem = MapItemList[v];
                var info = Model.MapItemInfoList[v];
                var image = mapItem.Go.GetComponent<Image>();
                image.color = Color.green; 
                
                if (info.TransmissionIndex != -1)
                {
                    image.color = Color.magenta; 
                }
            }
            View.Text.text = "请点击一个目标地点";
        }
        
        //重新还原地图
        public void GenMapFromConfig()
        {
            //var cfg = ConfigManage.Instance.ConfigAll.TbMap[10000];
            //for (int i = 0; i < Model.MapItemInfoList.Count; i++)
            //{
            //    var cfgV = cfg.ExchangeList[i];
            //    Model.MapItemInfoList[i].Cost = cfgV.Cost;
            //    Model.MapItemInfoList[i].TransmissionIndex = cfgV.TransmissionIndex;
            //    Model.MapItemInfoList[i].MapState = MapState.Empty;
            //}
        }
        
        //随机生成不可通行区域
        public void GenRandomObstacles()
        {
            for (int x = 0; x < 20; x++)
            {
                var index = Random.Range(0, MaxX * MaxY);
                Model.MapItemInfoList[index].MapState = MapState.Occupied;
            }
        }
        
        //随机生成代价
        public void GenRandomCost()
        {
            for (int x = 0; x < 100; x++)
            {
                var index = Random.Range(0, MaxX * MaxY);
                Model.MapItemInfoList[index].Cost = Random.Range(3, 20);
            }
        }
        
        //随机生成n组传送门
        public void GenRandomTransmission()
        {
            int i = 0;

            while (i < 1)
            {
                var indexA = Random.Range(0, MaxX * MaxY);
                var indexB = Random.Range(0, MaxX * MaxY);
                if (Model.MapItemInfoList[indexA].TransmissionIndex <0 && Model.MapItemInfoList[indexB].TransmissionIndex < 0)
                {
                    Model.MapItemInfoList[indexA].TransmissionIndex = indexB;
                    Model.MapItemInfoList[indexB].TransmissionIndex = indexA;
                    i++;
                }
            }
        }
        
        public int Pos2Index(int x, int y)
        {
            return y * MaxX + x;
        }
        
        public int Pos2Index(Vector2 pos)
        {
            var y = (int)pos.y;
            var x = (int)pos.x;
            return y * MaxX + x;
        }
        
        public Vector2 Index2Pos(int index)
        {
            var x = index % MaxX;
            var y = (index - x) / MaxX;
            return new Vector2(x,y);
        }
        
        public bool CheckPos(Vector2 pos)
        {
            if (!(pos.x >= 0 && pos.x < MaxX && pos.y >= 0 && pos.y <MaxY))
            {
                return false;
            }
            var index = Pos2Index(pos);
            if (index < 0 || index >= Model.MapItemInfoList.Count)
            {
                return false;
            }
            return Model.MapItemInfoList[index] != null;
        }
        
        //检查两个方向的关系
        public bool CheckPosDirections(int selfPos,int targetPos,Vector2 directions)
        {
            var index = Index2Pos(selfPos);
            if (Pos2Index(index+directions) == targetPos)
            {
                return true;
            }

            return false;
        }
        
        //返回一个位置周围的位置
        public List<int> GetAround(int index)
        {
            var pos = Index2Pos(index);
            List<int> around = new List<int>();
            CheckPosDelegate checkDelegate = (x) => {             
                if (CheckPos(x))
                {
                    around.Add(Pos2Index(x)); 
                } 
            };
            checkDelegate(pos+Vector2.up);
            checkDelegate(pos+Vector2.down);
            checkDelegate(pos+Vector2.left);
            checkDelegate(pos+Vector2.right);
            return around;
        }
        
        //返回一个位置周围的可达位置
        public List<int> GetReachable(int index)
        {
            var reachable = GetAround(index);
            if (Model.MapItemInfoList[index].TransmissionIndex != -1)
            {
                reachable.AddRange(GetAround(Model.MapItemInfoList[index].TransmissionIndex)); 
            }
            return reachable;
        }
        
        //返回一组位置中代价最小的位置
        public int GetMinCost(List<int> around)
        {
            var mapItemInfoList = Model.MapItemInfoList;
            int minCostIndex = around[0];
            foreach (var v in around)
            {
                if (mapItemInfoList[minCostIndex].Cost > mapItemInfoList[v].Cost)
                {
                    minCostIndex = v;
                }
            }

            return minCostIndex;
        }
        
        // 检查墙壁的GetAround
        public List<int> GetAroundCheckWell(int index)
        {
            var pos = Index2Pos(index);
            List<int> around = new List<int>();

            void CheckDelegate(Vector2 x)
            {
                if (CheckPos(x) && Model.MapItemInfoList[Pos2Index(x)].MapState == MapState.Empty)
                {
                    around.Add(Pos2Index(x));
                }
            }

            CheckDelegate(pos+Vector2.up);
            CheckDelegate(pos+Vector2.down);
            CheckDelegate(pos+Vector2.left);
            CheckDelegate(pos+Vector2.right);
            return around;
        }
        
        public void BFS()
        {
            var start = StartPos;
            var target = TargetPos;
            Dictionary<int,bool> mapDict = new Dictionary<int, bool>();
            List<int> mapList = new List<int>();
            List<int> preAddList = new List<int>();
            GetAroundDelegate getAroundInfo = GetAroundCheckWell;
            mapDict.Add(start,false);
            mapList.Add(start);
            int lastIndex = 0;
            int i = 0;
            while (true)
            {
                string str = "";
                preAddList.Clear();
                for (int j = lastIndex; j < mapList.Count; j++,lastIndex++)
                {
                    var v = mapList[j];
                    if (!mapDict.ContainsKey(v))
                    {
                        mapDict.Add(v,false);
                    }
                    if (mapDict[v]) continue;
                    
                    var around = getAroundInfo(v);
                    preAddList.AddRange(around);
                    mapDict[v] = true;
                    Model.MapItemInfoList[v].StepsIndex = i;
                    str = str + $"|{Index2Pos(v)}";
                }
                str = str + $"::{i}-{preAddList.Count}";
                Debug.Log(str);
                if (preAddList.Count <= 0)
                {
                   break; 
                }

                if (mapDict.ContainsKey(target) && mapDict[target])
                {
                   break; 
                }
                
                mapList.AddRange(preAddList);
                i++;
            }
            
            //从终点反向查找起点
            mapList.Clear();
            mapDict.Clear();
            mapList.Add(TargetPos);
            lastIndex = 0;
            i = 0;
            while (true)
            {
                string str = "";
                preAddList.Clear();
                for (int j = lastIndex; j < mapList.Count; j++,lastIndex++)
                {
                    var v = mapList[j];
                    if (!mapDict.ContainsKey(v))
                    {
                        mapDict.Add(v,false);
                    }
                    if (mapDict[v]) continue;
                    
                    var around = getAroundInfo(v);
                    foreach (var aroundIndex in around)
                    {
                        if (Model.MapItemInfoList[aroundIndex].StepsIndex == Model.MapItemInfoList[v].StepsIndex - 1)
                        {
                            preAddList.Add(aroundIndex);
                            break;
                        }
                    }
                    mapDict[v] = true;
                    str = str + $"|{Index2Pos(v)}";
                }
                str = str + $"::{i}-{preAddList.Count}";
                // Debug.Log(str);
                if (preAddList.Count <= 0)
                {
                    break; 
                }

                Path.AddRange(preAddList);
                if (mapDict.ContainsKey(start) && mapDict[start])
                {
                    break; 
                }
                
                mapList.AddRange(preAddList);
                i++;
            } 
        }
        
        public void Dijkstra()
        {
            var start = StartPos;
            var target = TargetPos;
            //记录整张地图的权值
            int[] s = new int[MaxX*MaxY];
            //记录哪些节点是已经找过的 值为findMapList中的索引
            bool[] mapFindFlag = new bool[MaxX*MaxY];
            //记录每个节点的最短路径上一节点坐标
            int[] pre = new int[MaxX*MaxY];
            //记录哪些节点是要找的
            List<int> findMapList = new List<int>();
            //初始化
            for (int j = 0; j < MaxX*MaxY; j++)
            {
                s[j] = 10000;
                mapFindFlag[j] = false;
                pre[j] = start;
            }
            GetAroundDelegate getAroundInfo = GetReachable;
            //设置起点
            s[start] = 0;
            findMapList.Add(start);
            int stepsNum = 0;
            //整个循环次数是地图的从次数，但不能用遍历，遍历是错误的用法，效率和正确性都会有影响
            while (stepsNum < MaxX*MaxY)
            {
                var index = 0;
                var sIndex = findMapList[index];
                //获取一个拥有最小权值的且没有记录过的节点
                while (mapFindFlag[sIndex])
                {
                    index++;
                    sIndex = findMapList[index];
                }
                //获取临近节点列表
                var iAround = getAroundInfo(sIndex);
                foreach (var v in iAround)
                {
                    //更新临近节点的权值,同时变更最短路径上一节点为当前节点
                    //新发现的边界加入到边缘集合中 (已经查找过的当然不算边缘)
                    if (!mapFindFlag[v] && (s[v] > (Model.MapItemInfoList[v].Cost + s[sIndex])))
                    {
                        pre[v] = sIndex;
                        s[v] = Model.MapItemInfoList[v].Cost + s[sIndex];
                        findMapList.Add(v);
                    }
                }
                //标记为记录
                mapFindFlag[sIndex] = true;
                Model.MapItemInfoList[sIndex].StepsIndex = stepsNum;
               
                //找到终点提前返回
                if (mapFindFlag[target])
                {
                    break; 
                }
                //手动排序实现优先队列，保证每次都是从最短路径出发
                findMapList.Sort(delegate(int x, int y)
                {
                    if (s[x] > s[y])
                    {
                        return 1;
                    }else if (s[x] == s[y])
                    {
                        return 0;
                    }

                    return -1;
                });
                stepsNum++;
            }
            Debug.Log(stepsNum);

            //获取路径
            var prePos = pre[target];
            Debug.Log(s[target]);
            while (prePos!=start)
            {
                Path.Add(prePos);
                prePos = pre[prePos];
            }
        }
        
        public void AStart()
        {
            var start = StartPos;
            var target = TargetPos;
            //记录整张地图的权值
            int[] s = new int[MaxX*MaxY];
            //记录哪些节点是已经找过的 值为findMapList中的索引
            bool[] mapFindFlag = new bool[MaxX*MaxY];
            //记录每个节点的最短路径上一节点坐标
            int[] pre = new int[MaxX*MaxY];
            //记录哪些节点是要找的
            List<int> findMapList = new List<int>();
            //初始化
            for (int j = 0; j < MaxX*MaxY; j++)
            {
                s[j] = 10000;
                mapFindFlag[j] = false;
                pre[j] = start;
            }
            GetAroundDelegate getAroundInfo = GetReachable;
            //设置起点
            s[start] = 0;
            findMapList.Add(start);
            int stepsNum = 0;
            //整个循环次数是地图的从次数，但不能用遍历，遍历是错误的用法，效率和正确性都会有影响
            while (stepsNum < MaxX*MaxY)
            {
                var index = 0;
                var sIndex = findMapList[index];
                //获取一个拥有最小权值的且没有记录过的节点
                while (mapFindFlag[sIndex])
                {
                    index++;
                    sIndex = findMapList[index];
                }
                //获取临近节点列表
                var iAround = getAroundInfo(sIndex);
                foreach (var v in iAround)
                {
                    //更新临近节点的权值(权值=起点权值+终点权值),同时变更最短路径上一节点为当前节点
                    //新发现的边界加入到边缘集合中 (已经查找过的当然不算边缘)
                    if (!mapFindFlag[v] && (s[v] > (Model.MapItemInfoList[v].Cost + s[sIndex])))
                    {
                        pre[v] = sIndex;
                        s[v] = Model.MapItemInfoList[v].Cost + s[sIndex]+Heuristic(target,v);
                        findMapList.Add(v);
                    }
                }
                //标记为记录
                mapFindFlag[sIndex] = true;
                Model.MapItemInfoList[sIndex].StepsIndex = stepsNum;
               
                //找到终点提前返回
                if (mapFindFlag[target])
                {
                    break; 
                }
                //手动排序实现优先队列，保证每次都是从最短路径出发
                findMapList.Sort(delegate(int x, int y)
                {
                    if (s[x] > s[y])
                    {
                        return 1;
                    }else if (s[x] == s[y])
                    {
                        return 0;
                    }

                    return -1;
                });
                stepsNum++;
            }
            Debug.Log(stepsNum);

            //获取路径
            var prePos = pre[target];
            while (prePos!=start)
            {
                Path.Add(prePos);
                prePos = pre[prePos];
            }
        }
        
        public int Heuristic(int targetPos,int pos)
        {
            var a = Index2Pos(targetPos);
            var b = Index2Pos(pos);
            return (int)(Mathf.Abs(a.x - b.x) + Mathf.Abs(a.y - b.y));
        }

        public void ShowMapInfo()
        {
            var str = "地图数据\n";
            foreach (var v in Model.MapItemInfoList)
            {
                str += $"{v.Cost},{v.TransmissionIndex};";
            } 
        }
    }
}
